/**
 * 
 */
package com.vision.core.sv.serv.dispatch;

import java.sql.Timestamp;
import java.util.List;
import java.util.Set;

import net.customware.gwt.dispatch.server.ActionHandler;
import net.customware.gwt.dispatch.server.ExecutionContext;
import net.customware.gwt.dispatch.shared.Action;
import net.customware.gwt.dispatch.shared.Result;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;

import com.vision.core.cm.data.ActivityType;
import com.vision.core.cm.data.Domain;
import com.vision.core.cm.db.data.ActivityLog;
import com.vision.core.cm.db.data.Permission;
import com.vision.core.cm.db.data.User;
import com.vision.core.cm.exception.DispatchException;
import com.vision.core.cm.exception.RpcException;
import com.vision.core.sv.db.mgr.IDataManager;
import com.vision.core.sv.serv.util.ServletUtils;
import com.vision.core.sv.util.BeanSerializer;

/**
 * Base {@link ActionHandler} class.
 * 
 * @author Mark
 *
 */
public abstract class BaseActionHandler<T, A extends Action<R>, R extends Result> implements ActionHandler<A, R> {

	private static final Logger log = LoggerFactory.getLogger(BaseActionHandler.class);
	
	protected IDataManager<T> dataManager;
	
	@Autowired
	@Qualifier("activityLogManager")
	protected IDataManager<ActivityLog<T>> activityLogManager;
	
	public IDataManager<T> getDataManager() {
		return dataManager;
	}
	
	public void setDataManager(IDataManager<T> dataManager) {
		this.dataManager = dataManager;
	}
	
	/**
	 * Null implementation for the {@link #rollback(Action, Result, ExecutionContext)}
	 * method. Subclasses may override this to provide rollback support.
	 * 
	 * @param action
	 * @param result
	 * @param context
	 */
	@Override
	public void rollback(A action, R result, ExecutionContext context) throws DispatchException {
		// Does nothing
	}
	
	/**
	 * Returns the {@link Domain} associated with this handler. This is used for
	 * logging events.
	 * 
	 * @return
	 */
	protected abstract Domain getDomain();
	
	/**
	 * Logs a "create" operation into the database.
	 * 
	 * @param object
	 */
	protected void logCreate(T object) {
		String serialized = BeanSerializer.serialize(object);
		
		log.debug("Logging CREATE operation: {}", serialized);
			
		ActivityLog<T> aLog = new ActivityLog<T>(getLoggedInUser(), now(), getDomain(), ActivityType.CREATE, serialized);
		activityLogManager.create(aLog);
	}
	
	/**
	 * Logs an "update" operation into the database.
	 * 
	 * @param oldObject
	 * @param newObject
	 */
	protected void logUpdate(T oldObject, T newObject) {
		String serialized = BeanSerializer.serialize(newObject);
		
		log.debug("Logging UPDATE operation: {}.", serialized);
		
		ActivityLog<T> aLog = new ActivityLog<T>(getLoggedInUser(), now(), getDomain(), ActivityType.UPDATE, serialized);
		activityLogManager.create(aLog);
	}
	
	/**
	 * Logs a "delete" operation into the database.
	 * 
	 * @param object
	 */
	protected void logDelete(T object) {
		String serialized = BeanSerializer.serialize(object);
		
		log.debug("Logging DELETE operation: {}", serialized);
		
		ActivityLog<T> aLog = new ActivityLog<T>(getLoggedInUser(), now(), getDomain(), ActivityType.DELETE, serialized);
		activityLogManager.create(aLog);
	}
	
	/**
	 * Logs an "associate" operation into the database.
	 * 
	 * @param object
	 */
	protected <O> void logAssociate(T object, List<O> associatedObjects) {
		String serialized = BeanSerializer.serialize(object);
		
		log.debug("Logging ASSOCIATE operation: {}", serialized);
		
		ActivityLog<T> aLog = new ActivityLog<T>(getLoggedInUser(), now(), getDomain(), ActivityType.ASSOCIATE, serialized);
		activityLogManager.create(aLog);
	}
	
	/**
	 * Logs a "deassociate" operation into the database.
	 * 
	 * @param object
	 */
	protected <O> void logDeassociate(T object, List<O> deassociatedObjects) {
		String serialized = BeanSerializer.serialize(object);
		
		log.debug("Logging DEASSOCIATE operation: {}", serialized);
		
		ActivityLog<T> aLog = new ActivityLog<T>(getLoggedInUser(), now(), getDomain(), ActivityType.DEASSOCIATE, serialized);
		activityLogManager.create(aLog);
	}
	
	// Helpers %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
	
	protected static User getLoggedInUser() {
		return ServletUtils.getLoggedInUser();
	}
	
	protected static Set<Permission> getLoggedInUsersPermissions() {
		return ServletUtils.getLoggedInUsersPermissions();
	}
	
	protected static void checkPermissions(String ... requiredPermissions) throws RpcException {
		ServletUtils.checkPermissions(false, requiredPermissions);
	}
	
	protected static void checkPermissions(boolean matchAll, String ... requiredPermissions) throws RpcException {
		ServletUtils.checkPermissions(matchAll, requiredPermissions);
	}
	
	protected static void checkPermissions(Permission ... requiredPermissions) throws RpcException {
		ServletUtils.checkPermissions(false, requiredPermissions);
	}
	
	protected static void checkPermissions(boolean matchAll, Permission ... requiredPermissions) throws RpcException {
		ServletUtils.checkPermissions(matchAll, requiredPermissions);
	}
	
	public static Timestamp now() {
		return new Timestamp(System.currentTimeMillis());
	}

}
